package ink.ykb.snowflake.config;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class SnowflakeIdUtil implements ApplicationListener<WebServerInitializedEvent>{
	
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	
	private int port;
	
	private String ip;
	
	private String hostName;
	
	private SnowflakeId snowflakeId;
	
	private SnowflakeIdWorker snowflakeIdWorker;
	
	/** SNOWFLAKE_PREFIX：snowflake:*/
	public final static String SNOWFLAKE_PREFIX = "snowflake:";
	
	/** 工作码 */
	public final static long MAX_WORKER_ID = 32;
	
	/** 机器码 */
	public final static long MAX_DATACENTER_ID = 32;
	
	/** Redis缓存超时时间 7天*/
	public final static long CACHE_TIME =  7 * 24 * 3600 * 1000;
	
	/** 更新缓存时间 1天*/
	public final static long LAST_UPDATE_TIME =  1 * 24 * 3600 * 1000;
	
	public void init() {
		if(snowflakeId == null || snowflakeId.getNextTimeOut() < System.currentTimeMillis()) {
			synchronized (this) {
					if(snowflakeId == null || snowflakeId.getNextTimeOut() < System.currentTimeMillis()) {
						initIds();
						log.info("SnowflakeId更新：{}",this.print());
					}
			}
		}
		
		if(this.snowflakeIdWorker == null) {
			synchronized (this) {
				if(this.snowflakeIdWorker == null) {
					this.snowflakeIdWorker = new SnowflakeIdWorker(this.snowflakeId.workerId, this.snowflakeId.datacenterId);
				}
			}
		}else {
			 if(this.snowflakeId.workerId != this.snowflakeIdWorker.getWorkerId() || this.snowflakeId.datacenterId != this.snowflakeIdWorker.getDatacenterId()) {
				 synchronized (this) {
					 if(this.snowflakeId.workerId != this.snowflakeIdWorker.getWorkerId() || this.snowflakeId.datacenterId != this.snowflakeIdWorker.getDatacenterId()) {
						 this.snowflakeIdWorker = new SnowflakeIdWorker(this.snowflakeId.workerId, this.snowflakeId.datacenterId);
					 }
				 }
			 }
		}
		
	}
	
	public void initIds() {
		
		String app = SNOWFLAKE_PREFIX +this.hostName+":"+ this.ip +":" + this.port;
		String ids =  (String) this.stringRedisTemplate.boundValueOps(app).get();
		if(ids == null) {
			//循环竞争获取
			for (long workerId = 0; workerId < MAX_WORKER_ID; workerId++) {
				for (long datacenterId = 0; datacenterId < MAX_DATACENTER_ID; datacenterId++) {
					String newIds = SNOWFLAKE_PREFIX + workerId+":"+datacenterId;
					 boolean e = stringRedisTemplate.boundValueOps(newIds).setIfAbsent(app).booleanValue();
					 if(e) {
						 //设置newIds-app 时间，插入app-newIds
						 updateSnowflakeId(workerId, datacenterId, app);
						 return;
					 }
				}
			}
			
			//TODO 有一种可能是存在没有释放的ids-app,多个ids对应一个app
			//如果循环所有都没获取到ids，再查一下当前服务是否存在未释放的ids
			for (long workerId = 0; workerId < MAX_WORKER_ID; workerId++) {
				for (long datacenterId = 0; datacenterId < MAX_DATACENTER_ID; datacenterId++) {
					String newIds =SNOWFLAKE_PREFIX + workerId+":"+datacenterId;
					String newApp = (String) this.stringRedisTemplate.boundValueOps(newIds).get();
					if(app.equals(newApp)) {
						//插入newIds-app，插入app-newIds
						 boolean e = stringRedisTemplate.boundValueOps(newIds).setIfAbsent(newApp).booleanValue();
						 if(e) {
							 //设置newIds-app 时间，插入app-newIds
							 updateSnowflakeId(workerId, datacenterId, app);
							 return;
						 }
					 }
				}
			}
			
			throw new RuntimeException("未获取到机器码ID和工作码ID，部署项目实例超过1024台或有部分项目实例的ids没有释放，需新增前缀或删除无效ID");
			
		}else {
			
			String newApp = (String) this.stringRedisTemplate.boundValueOps(ids).get();
			if(newApp != null && newApp.equals(app)) {
				String[] split = ids.split(":");
				long workerId = Long.parseLong(split[1]);
				long datacenterId = Long.parseLong(split[2]);
				//更新newIds-app时间，更新app-newIds时间
				updateSnowflakeId(workerId, datacenterId, app);
				
			}else {//ids 对应的app不存在或者不相同时删除app-ids
				this.stringRedisTemplate.delete(app);
				initIds();
			}
		}
	}
	
	public void updateSnowflakeId(long workerId,long datacenterId,String app) {
		String newIds =SNOWFLAKE_PREFIX + workerId+":"+datacenterId;
		
		this.stringRedisTemplate.opsForValue().set(app,newIds, new Date(CACHE_TIME - 1000).getTime(), TimeUnit.MILLISECONDS);//-1s确保app-ids存在时，ids-app一定存在
		 this.stringRedisTemplate.opsForValue().set(newIds, app, new Date(CACHE_TIME).getTime(), TimeUnit.MILLISECONDS);
		 
		 if(snowflakeId == null ) {
			 snowflakeId = new SnowflakeId(workerId, datacenterId);
		 }else {
			 if(this.snowflakeId.workerId != workerId || this.snowflakeId.datacenterId != datacenterId) {
				 snowflakeId = new SnowflakeId(workerId, datacenterId);
			 }
		 }
		 snowflakeId.setNextTimeOut(new Date(System.currentTimeMillis() + LAST_UPDATE_TIME).getTime());
	}
	
	public Long nextId() {
		init();
		return this.snowflakeIdWorker.nextId();
	}

	@Override
	public void onApplicationEvent(WebServerInitializedEvent event) {
		this.port = event.getWebServer().getPort();
		getIp();
		init();
	}
	
	public void getIp() {
			try {   
				List<String> ipList = new ArrayList<>();
				List<String> hostNameList = new ArrayList<>();
				Enumeration<NetworkInterface>  netInterfaces = NetworkInterface.getNetworkInterfaces();   
				
			    while (netInterfaces.hasMoreElements()) {   
			        NetworkInterface ni = netInterfaces.nextElement();   
			        Enumeration<InetAddress> ips = ni.getInetAddresses();   
			        while (ips.hasMoreElements()) {   
			        	 InetAddress inetAddress = ips.nextElement();
			        	if (!inetAddress.isLoopbackAddress()
				        			&& inetAddress.getHostAddress().indexOf(":") == -1) {
			        		
				        		ipList.add(inetAddress.getHostAddress());
				        		hostNameList.add(inetAddress.getHostName());
				         }
			        }   
			    }
			    // 取第一个，或给一个正则表达式优先匹配
			    this.ip =  ipList.get(0); 
			    this.hostName =  hostNameList.get(0);
			} catch (Exception e) {   
			    e.printStackTrace();   
			}  
	}
	
	private  class SnowflakeId {
		
		  /** 机器id */
			private  long workerId;

		    /** 数据标识id*/
		    private long datacenterId;
		    
		    /** 更新超时时间 */
		    private long nextTimeOut;
		    
			public SnowflakeId(long workerId, long datacenterId) {
				super();
				this.workerId = workerId;
				this.datacenterId = datacenterId;
			}

			public long getNextTimeOut() {
				return nextTimeOut;
			}
			
			public void setNextTimeOut(long nextTimeOut) {
				this.nextTimeOut = nextTimeOut;
			}

			public long getWorkerId() {
				return workerId;
			}

			public long getDatacenterId() {
				return datacenterId;
			}

			
	}
	
	public String  print() {
		
		return "{"
				+ "\"app\":\""+SNOWFLAKE_PREFIX+this.hostName+":"+this.ip+":"+this.port+ "\""+","
				+ "\"ids\":\""+SNOWFLAKE_PREFIX+this.snowflakeId.workerId+":"+this.snowflakeId.datacenterId+ "\""+","
				+ "\"workerId\":\""+this.snowflakeId.workerId+ "\""+","
				+ "\"datacenterId\":\""+this.snowflakeId.datacenterId+ "\""+","
				+ "\"nextTimeOut\":\""+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(new Date(this.snowflakeId.nextTimeOut))+"\""
				+ "}";
	}
	
}
